package flexibilityDesign;

import java.math.BigDecimal;

import util.NullOutputStream;
import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;

public class ChainingDesign {
	private int n;// product number
	private int m;// resource number
	private int M;
	private double[][] demands;
	private double[][] pi;
	private double[] capacities;
	private double[][] c;// link cost

	public ChainingDesign(int n, int m, int M) {
		this.n = n;
		this.m = m;
		this.M = M;
	}

	/**
	 * y_{ij}=1 means product i can be produced by plant j
	 * **/
	private int[][] setFlexibilityConfig() {
		int[][] y = new int[n][m];
		for (int i = 0; i < m; i++) {
			for (int j = 0; j < m; j++) {
				if (i == j || (i != m - 1 && j == i + 1)
						|| (i == m - 1 && j == 0))
					y[i][j] = 1;
				else
					y[i][j] = 0;
			}
		}
		int ex = n - m;
		for (int i = 0; i < ex; i++) {
			int fj = n / 2 / ex * i;
			int ej = fj + m / 2;
			for (int j = 0; j < m; j++) {
				if (j == fj || j == ej)
					y[m + i][j] = 1;
				else
					y[m + i][j] = 0;
			}
		}
		return y;
	}

	private void initData() {
		double cap = BigDecimal.valueOf(n * 100)
				.divide(BigDecimal.valueOf(m), 5, BigDecimal.ROUND_HALF_EVEN)
				.doubleValue();
		this.capacities = new double[m];
		for (int j = 0; j < m; j++)
			this.capacities[j] = cap;
		this.demands = Support.readDemands(M, n);
		this.c = Support.readLinkCost(n, m);
		this.pi = Support.readProfitMargin(n, m);
	}

	public double calculateCost() {
		BigDecimal value = BigDecimal.ZERO;

		this.initData();
		int[][] y = this.setFlexibilityConfig();

		try {
			for (int it = 0; it < M; it++) {
				IloCplex cplex = new IloCplex();
				cplex.setOut(new NullOutputStream());
				IloNumVar[][] x = new IloNumVar[n][m];
				for (int i = 0; i < n; i++)
					for (int j = 0; j < m; j++)
						if (y[i][j] == 0)
							x[i][j] = cplex.numVar(0.0, 0.0);
						else
							x[i][j] = cplex.numVar(0.0, Double.MAX_VALUE);

				IloLinearNumExpr obj = cplex.linearNumExpr();
				for (int i = 0; i < n; i++) {
					for (int j = 0; j < m; j++) {
						obj.addTerm(-pi[i][j], x[i][j]);

						cplex.addLe(x[i][j], y[i][j] * capacities[j]);
					}
				}
				cplex.addMinimize(obj);

				for (int i = 0; i < n; i++) {
					IloLinearNumExpr tp = cplex.linearNumExpr();
					for (int j = 0; j < m; j++) {
						if (y[i][j] == 1)
							tp.addTerm(1.0, x[i][j]);
					}
					cplex.addLe(tp, demands[it][i]);
				}
				for (int j = 0; j < m; j++) {
					IloLinearNumExpr tc = cplex.linearNumExpr();
					for (int i = 0; i < n; i++)
						if (y[i][j] == 1)
							tc.addTerm(1.0, x[i][j]);
					cplex.addLe(tc, capacities[j]);
				}
				if (cplex.solve()) {
					value = value.add(BigDecimal.valueOf(cplex.getObjValue()));
				}
				cplex.end();
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		value = value.divide(BigDecimal.valueOf(M), 5,
				BigDecimal.ROUND_HALF_EVEN);

		for (int i = 0; i < n; i++)
			for (int j = 0; j < m; j++)
				if (y[i][j] == 1)
					value = value.add(BigDecimal.valueOf(c[i][j]));

		return value.doubleValue();
	}
}
